#ifndef JJB_NODE
#define JJB_NODE

#include <cassert> 
#include <set>

#include "record.h"

using namespace std;


class node
{
public:
    node() : left(NULL), right(NULL), countLESum(0), countGTSum(0)
            {countLE[0] = 0; countLE[1] = 0; countLE[2] = 0; countLE[3] = 0; countLE[4] = 0;
             countGT[0] = 0; countGT[1] = 0; countGT[2] = 0; countGT[3] = 0; countGT[4] = 0;}

    node(node* root) : left(NULL), right(NULL), field(root->field),
             nValue(root->nValue), values(root->values), countLESum(0), countGTSum(0) 
            {countLE[0] = 0; countLE[1] = 0; countLE[2] = 0; countLE[3] = 0; countLE[4] = 0;
             countGT[0] = 0; countGT[1] = 0; countGT[2] = 0; countGT[3] = 0; countGT[4] = 0;}

    node(int tmpFld, double tmpValue)
           : left(NULL), right(NULL), field(tmpFld),
             nValue(tmpValue), countLESum(0), countGTSum(0)
            {assert(tmpFld == 0 || tmpFld >=4);
             countLE[0] = 0; countLE[1] = 0; countLE[2] = 0; countLE[3] = 0; countLE[4] = 0;
             countGT[0] = 0; countGT[1] = 0; countGT[2] = 0; countGT[3] = 0; countGT[4] = 0;}

    node(int tmpFld, set<int> tmpValues)
           : left(NULL), right(NULL), field(tmpFld),
             nValue(0), values(tmpValues), countLESum(0), countGTSum(0)
            {assert(tmpFld == 1 || tmpFld == 2 || tmpFld == 3);
             countLE[0] = 0; countLE[1] = 0; countLE[2] = 0; countLE[3] = 0; countLE[4] = 0;
             countGT[0] = 0; countGT[1] = 0; countGT[2] = 0; countGT[3] = 0; countGT[4] = 0;}


    ~node() { if (left) delete(left); if (right) delete(right); }

    // Update the counts of positive and negative training records
    void processTrainingRecord(record* theRecord);

    // Is value for this record less than or equal to threshold (or in the list of categories
    bool evaluatesLeft(record* theRecord);

    // Evaluate test record
    int evaluateTestRecord(record* theRecord);

    int getField() {return field;}
    void setField(int tmp) {assert(tmp >= 0 && tmp < MAX_FIELD - 1); field = tmp;}
    double getValue() { return nValue;}
    set<int>& getValues() { return values;}
    void setValue(double tmp) { assert(field == 0 || field >= 4); nValue = tmp;}
    void insertValue(int tmp) { assert(field == 1 || field == 2 || field == 3);
                                   values.insert(tmp);}
    // BUG:  For categorical items, we need an analagous
    // collection of values
    node* getLeft() {return left;}
    node* getRight() {return right;}
   
    void setLeft(node* tmpLeft) { left = tmpLeft; }
    void setRight(node* tmpRight) { right = tmpRight; }

    int getLEObservationCount() { return countLESum; }
    int getGTObservationCount() { return countGTSum; }

    int getLEObservationCount(int i) { assert ( i >= 0 && i < 5); return countLE[i]; }
    int getGTObservationCount(int i) { assert ( i >= 0 && i < 5); return countGT[i]; }

    // Return category with most observations for each branch
    int getLEWinner();
    int getGTWinner();
private:
    node* left;
    node* right;
    int field;
    double nValue;   // The threshold value for continuous-valued fields
    set<int> values; // For symbolic fields, the collection of values 

    int countGTSum; // Count of records with field value greater than nValue
    int countGT[5]; // Count of records for each type with field value greater than nValue
    int countLESum; // Count of records with field value <= nValue
    int countLE[5]; // Count of records for each type with field value <= nValue
};

node* copySubtree(node* root);

#endif
